package com.ruoyi.project.system.student.controller;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

import com.ruoyi.project.system.student.domain.SnowflakeIdWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.project.system.student.domain.Student;
import com.ruoyi.project.system.student.service.IStudentService;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.page.TableDataInfo;

import javax.annotation.Resource;

/**
 * 【请填写功能名称】Controller
 *
 * @author ruoyi
 * @date 2021-10-09
 */
@Controller
@RequestMapping("/system/student")
@Slf4j
public class StudentController extends BaseController
{
    private String prefix = "system/student";

    @Autowired
    private IStudentService studentService;

    @Resource
    private RedissonClient redissonClient;//配置文件是redisson-single.yml

    @Resource
    private StringRedisTemplate stringRedisTemplate;//配置文件是application.yml
    //@Cacheable的配置文件也是application.yml  application.yml和redisson-single.yml使用同一个库

    @RequiresPermissions("system:student:view")
    @GetMapping()
    public String student()
    {
        return prefix + "/student";
    }

    //====================先查询,缓存数据,再修改.发现缓存数据不是最新====================start=============================
    /**
     * 查询【请填写功能名称】列表
     * key='#name' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'
     * condition 不指定相当于 true，unless 不指定相当于 false
     * 当 condition = false，一定不会缓存；
     * 当 condition = true，且 unless = true，不缓存；
     * 当 condition = true，且 unless = false，缓存；(condition 不指定相当于 true，unless 不指定相当于 false.所以可以不写)
     * sync = true 可以有效的避免缓存击穿的问题 --多线程访问的时候,可以一个线程运算,其他线程等待结果
     */
    @RequiresPermissions("system:student:list")
    @GetMapping("/list")
    @ResponseBody
    //@Cacheable(value = "student#60",key = "#name")//#60指的是key(student)的过期时间时60秒
    @Cacheable(value = "student",key = "#id")//#60指的是key(student)的过期时间时60秒
    public AjaxResult list(Long id)
    {
        // 设置字符串
//        RBucket<Integer> keyObj = redissonClient.getBucket("yanghua");
//        keyObj.set(1);
//
//        String valueCount = stringRedisTemplate.opsForValue().get("yanghua");
//        System.out.println(valueCount);
        System.out.println("从数据库获取数据"+id);
        Student student = new Student();
        student.setId(id);
        startPage();
        List<Student> list = studentService.selectStudentList(student);
        return AjaxResult.success(list.get(0));
    }
    @RequiresPermissions("system:student:edit")
    @Log(title = "【请填写功能名称】", businessType = BusinessType.UPDATE)
    @GetMapping("/edit2")
    @ResponseBody
    @CachePut(value = "student",key = "#id")//更新联锁key下的缓存数据
    //@CacheEvict(value = "student",key = "#id")//删除联锁(student:#id)key下的缓存数据
    //@CacheEvict(value = "student")//删除key=student下的缓存数据
    public AjaxResult editSave2(Long id)//896444064826657249
    {
        Student student = new Student();
        student.setName("bb机");
        student.setId(id);
        int i = studentService.updateStudent(student);
        if (i>0){

        }
        return AjaxResult.success(student);
    }
    //========================================end=============================
    /**
     * 修改保存【请填写功能名称】
     */
    @RequiresPermissions("system:student:edit")
    @Log(title = "【请填写功能名称】", businessType = BusinessType.UPDATE)
    @PostMapping("/edit")
    @ResponseBody
    public AjaxResult editSave(Student student)
    {

        return toAjax(studentService.updateStudent(student));
    }


    /**
     * 导出【请填写功能名称】列表
     */
    @RequiresPermissions("system:student:export")
    @Log(title = "【请填写功能名称】", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    @ResponseBody
    public AjaxResult export(Student student)
    {
        List<Student> list = studentService.selectStudentList(student);
        ExcelUtil<Student> util = new ExcelUtil<Student>(Student.class);
        return util.exportExcel(list, "student");
    }

    /**
     * 新增【请填写功能名称】
     */
    @GetMapping("/add")
    public String add()
    {
        return prefix + "/add";
    }

    /**
     * 新增保存【请填写功能名称】
     */
    @RequiresPermissions("system:student:add")
    @Log(title = "【请填写功能名称】", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    @ResponseBody
    public AjaxResult addSave(Student student)
    {
        return toAjax(studentService.insertStudent(student));
    }

    /**
     * 修改【请填写功能名称】
     */
    @GetMapping("/edit/{id}")
    public String edit(@PathVariable("id") Long id, ModelMap mmap)
    {
        Student student = studentService.selectStudentById(id);
        mmap.put("student", student);
        return prefix + "/edit";
    }



    /**
     * 删除【请填写功能名称】
     */
    @RequiresPermissions("system:student:remove")
    @Log(title = "【请填写功能名称】", businessType = BusinessType.DELETE)
    @PostMapping( "/remove")
    @ResponseBody
    public AjaxResult remove(String ids)
    {
        return toAjax(studentService.deleteStudentByIds(ids));
    }

    private SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
    /**
     * 单线程批量插入4万数据,耗时1889毫秒
     */
    @GetMapping("/insertStudentBatch")
    @ResponseBody
    public AjaxResult insertStudentBatch()
    {
        long starttime = System.currentTimeMillis();

        List<Student> students = new ArrayList<>();
        //循环4万次,添加4万个学生,单线程插入需要多少时间;多线程插入需要多少时间(5万数据太多 直接报错)
        for (int i = 0; i <40000 ; i++) {
            Student student = new Student();
            student.setId(idWorker.nextId());
            student.setName("yanghua"+i);
            students.add(student);
        }

        int i = studentService.insertStudentBatch(students);
        log.info("插入一万条数据耗时:"+(System.currentTimeMillis()-starttime)); // 1889 , 1653  毫秒
        return toAjax(i);
    }
    /**
     * 多线程批量插入4万数据,
     * 不使用异步注解耗时1718 ,依然和单线程一样,整个过程虽被分割8段,但是同一个线程依次执行
     * 使用异步注解耗时16 ,从线程池获取了8个线程同时执行这8段list,理论时间应该是1718/8=213毫秒,但是实际值是16  远小于213
     * 方法体虽然立马执行了return,但是线程依然在执行业务没有得到释放,这个业务时间并非是实际值,这也是显示的16为啥远小于理论值.实际值其实不是实际值
     */
    @GetMapping("/insertStudentBatch2")
    @ResponseBody
    public Long insertStudentBatch2()
    {
        long starttime = System.currentTimeMillis();

        List<Student> students = new ArrayList<>();
        //循环4万次,添加4万个学生,单线程插入需要多少时间;多线程插入需要多少时间(5万数据太多 直接报错)
        for (int i = 0; i <40000 ; i++) {
            Student student = new Student();
            student.setId(idWorker.nextId());
            student.setName("yanghua"+i);
            students.add(student);
        }
        //对list分段插入,一组5000个  分8组  理论时间也是原来的8分之1,可能略多
        int size = students.size();//总个数
        int danzu=5000;  //一组5000个
        int countZu = size%danzu==0?size/danzu:(size/danzu+1) ;  //分出多少组,就是有几条线程
        Integer j =0 ;
        final CountDownLatch countDownLatch = new CountDownLatch(countZu);//线程个数计数器
        for (int i = 0; i < countZu ; i++) {
             //对list进行截取
            try{
                j += studentService.insertStudentBatch(students.subList(i * danzu, (i + 1) * danzu));
            }catch (Exception e){

            }finally {
                countDownLatch.countDown();//减1
            }

        }
        try {
            countDownLatch.await();//计数器为0,唤醒主线程
            long endtime = System.currentTimeMillis();
            long time = endtime - starttime;
            log.info("插入一万条数据耗时:"+(time)); // 毫秒
            log.info("插入{}条数据耗时:",j); // 返回值毫无意义

            return Long.valueOf(j);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


        return null;
    }
}
